﻿using Engine.Media;
using System.Text.RegularExpressions;

namespace Game
{
    //数据处理 
    public class DataHandle
    {
        public static Color[] ColorList =
        [
            new(255, 255, 255, 255),
            new(181, 255, 255, 255),
            new(255, 181, 255, 255),
            new(160, 181, 255, 255),
            new(255, 240, 160, 255),
            new(181, 255, 181, 255),
            new(255, 181, 160, 255),
            new(181, 181, 181, 255),
            new(112, 112, 112, 255),
            new(32, 112, 112, 255),
            new(70, 20, 70, 255),
            new(26, 52, 128, 255),
            new(87, 54, 31, 255),
            new(24, 116, 24, 255),
            new(136, 32, 32, 255),
            new(24, 24, 24, 255)
        ];

        public static Color[] ElectronColorList =
        [
            new(0, 0, 0),
            new(255, 255, 255),
            new(0, 255, 255),
            new(255, 0, 0),
            new(0, 0, 255),
            new(255, 240, 0),
            new(0, 255, 0),
            new(255, 120, 0),
            new(255, 0, 255)
        ];

        public static short[][] StairStates =
        [
            [1,1,1,1,1,0,1,0],
            [1,1,1,1,1,1,0,0],
            [1,1,1,1,0,1,0,1],
            [1,1,1,1,0,0,1,1],
            [1,0,1,0,1,1,1,1],
            [1,1,0,0,1,1,1,1],
            [0,1,0,1,1,1,1,1],
            [0,0,1,1,1,1,1,1],
            [1,1,1,1,0,0,1,0],
            [1,1,1,1,1,0,0,0],
            [1,1,1,1,0,1,0,0],
            [1,1,1,1,0,0,0,1],
            [0,0,1,0,1,1,1,1],
            [1,0,0,0,1,1,1,1],
            [0,1,0,0,1,1,1,1],
            [0,0,0,1,1,1,1,1],
            [1,1,1,1,1,0,1,1],
            [1,1,1,1,1,1,1,0],
            [1,1,1,1,1,1,0,1],
            [1,1,1,1,0,1,1,1],
            [1,0,1,1,1,1,1,1],
            [1,1,1,0,1,1,1,1],
            [1,1,0,1,1,1,1,1],
            [0,1,1,1,1,1,1,1]
        ];

        public static short[][] SlabStates =
        [
            [1,1,1,1,0,0,0,0],
            [0,0,0,0,1,1,1,1]
        ];

        //获取梯砖的值
        public static int GetStairValue(int value, int l)
        {
            int id = Terrain.ExtractContents(value);
            int data = Terrain.ExtractData(value);
            int? color = StairsBlock.GetColor(data);
            bool pass = (StairStates[StairsBlock.GetVariant(data)][l] == 1);
            switch (id)
            {
                case 49: id = 21; break;
                case 217: id = 3; break;
                case 48: id = 5; break;
                case 50: id = 26; break;
                case 76: id = 73; break;
                case 51: id = 4; break;
                case 69: id = 68; break;
                case 96: id = 67; break;
                default: return 0;
            }
            if (!pass) return 0;
            return (color.HasValue) ? Terrain.MakeBlockValue(id, 0, PaintedCubeBlock.SetColor(0, color.Value)) : id;
        }

        //获取半砖的值
        public static int GetSlabValue(int value, int l)
        {
            int id = Terrain.ExtractContents(value);
            int data = Terrain.ExtractData(value);
            int? color = SlabBlock.GetColor(data);
            bool pass = (SlabStates[SlabBlock.GetIsTop(data) ? 1 : 0][l] == 1);
            switch (id)
            {
                case 55: id = 21; break;
                case 136: id = 3; break;
                case 53: id = 5; break;
                case 54: id = 26; break;
                case 75: id = 73; break;
                case 52: id = 4; break;
                case 70: id = 68; break;
                case 95: id = 67; break;
                default: return 0;
            }
            if (!pass) return 0;
            return (color.HasValue) ? Terrain.MakeBlockValue(id, 0, PaintedCubeBlock.SetColor(0, color.Value)) : id;
        }

        //贴图颜色获取
        public static Color GetCommandColor(int color)
        {
            Color commandColor = Color.Green;
            switch (color)
            {
                case 0: commandColor = new Color(0, 255, 0); break;
                case 1: commandColor = new Color(181, 255, 255); break;
                case 2: commandColor = new Color(255, 181, 255); break;
                case 3: commandColor = new Color(160, 181, 255); break;
                case 4: commandColor = new Color(255, 240, 160); break;
                case 5: commandColor = new Color(181, 255, 181); break;
                case 6: commandColor = new Color(255, 181, 160); break;
                case 7: commandColor = new Color(181, 181, 181); break;
                case 8: commandColor = new Color(112, 112, 112); break;
                case 9: commandColor = new Color(0, 255, 255); break;
                case 10: commandColor = new Color(255, 0, 255); break;
                case 11: commandColor = new Color(0, 0, 255); break;
                case 12: commandColor = new Color(225, 112, 0); break;
                case 13: commandColor = new Color(0, 255, 0); break;
                case 14: commandColor = new Color(255, 0, 0); break;
                case 15: commandColor = new Color(24, 24, 24); break;
            }
            return commandColor;
        }

        //由颜色获取数据序号
        public static int GetColorIndex(Color c, int type = 0)
        {
            int index = 0;
            Color[] colorList = ColorList;
            if (type == 1) colorList = ElectronColorList;
            float[] arC = new float[colorList.Length];
            for (int i = 0; i < colorList.Length; i++)
            {
                Color c2 = colorList[i];
                //float cR = (c.R - c2.R) / 256f;
                //float cG = (c.G - c2.G) / 256f;
                //float cB = (c.B - c2.B) / 256f;
                //arC[i] = cR * cR + cG * cG + cB * cB;
                int r = (c.R + c2.R) / 2;
                int cR = (c.R - c2.R) * (c.R - c2.R);
                int cG = (c.G - c2.G) * (c.G - c2.G);
                int cB = (c.B - c2.B) * (c.B - c2.B);
                arC[i] = (2 + r / 256) * cR + 4 * cG + (2 + (255 - r) / 256) * cB;
            }
            float min = float.MaxValue;
            for (int k = 0; k < arC.Length; k++)
            {
                if (arC[k] < min)
                {
                    min = arC[k];
                    index = k;
                }
            }
            return index;
        }

        //数值转16进制信号
        public static string NumberToSignal(int n)
        {
            if (n < 10 && n >= 0) return n.ToString();
            else
            {
                switch (n)
                {
                    case 10: return "a";
                    case 11: return "b";
                    case 12: return "c";
                    case 13: return "d";
                    case 14: return "e";
                    case 15: return "f";
                    default: return "0";
                }
            }
        }

        //得到bool值
        public static bool GetBoolValue(string str)
        {
            return (str.ToLower() == "true");
        }

        //得到Vector2值
        public static Vector2 GetVector2Value(string str)
        {
            string[] arstr = str.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            return new Vector2(int.Parse(arstr[0]), int.Parse(arstr[1]));
        }

        //得到Vector3值
        public static Vector3 GetVector3Value(string str)
        {
            string[] arstr = str.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            return new Vector3(int.Parse(arstr[0]), int.Parse(arstr[1]), int.Parse(arstr[2]));
        }

        //得到Vector4值
        public static Vector4 GetVector4Value(string str)
        {
            string[] arstr = str.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            return new Vector4(int.Parse(arstr[0]), int.Parse(arstr[1]), int.Parse(arstr[2]), int.Parse(arstr[3]));
        }

        //得到Point2值
        public static Point2 GetPoint2Value(string str)
        {
            string[] arstr = str.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            return new Point2(int.Parse(arstr[0]), int.Parse(arstr[1]));
        }

        //得到Point3值
        public static Point3 GetPoint3Value(string str)
        {
            string[] arstr = str.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            return new Point3(int.Parse(arstr[0]), int.Parse(arstr[1]), int.Parse(arstr[2]));
        }

        //得到Color值
        public static Color GetColorValue(string str)
        {
            string[] arstr = str.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            if (arstr.Length == 3)
            {
                return new Color(int.Parse(arstr[0]), int.Parse(arstr[1]), int.Parse(arstr[2]));
            }
            else if (arstr.Length == 4)
            {
                return new Color(int.Parse(arstr[0]), int.Parse(arstr[1]), int.Parse(arstr[2]), int.Parse(arstr[3]));
            }
            else
            {
                return Color.White;
            }
        }

        //得到DateTime值
        public static DateTime GetDateTimeValue(string str)
        {
            string[] arstr = str.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            int year = Math.Clamp(int.Parse(arstr[0]), 2012, 2100);
            int month = Math.Clamp(int.Parse(arstr[1]), 1, 12);
            int day = Math.Clamp(int.Parse(arstr[2]), 1, 31);
            int hour = Math.Clamp(int.Parse(arstr[3]), 0, 23);
            int minute = Math.Clamp(int.Parse(arstr[4]), 0, 59);
            int second = Math.Clamp(int.Parse(arstr[5]), 0, 59);
            return new DateTime(year, month, day, hour, minute, second);
        }

        //得到自然数
        public static int GetNaturalValue(string str)
        {
            int nv = int.Parse(str);
            return ((nv < 0) ? 0 : nv);
        }

        //特殊符转义
        public static string CharacterEscape(string str)
        {
            return str.Replace("[n]", "\n").Replace("[e]", " ").Replace("[c]", ":").Replace("[d]", "=");
        }

        //是否包含指令变量
        public static bool IsContainsVariable(string str)
        {
            List<string> constValues = new();
            string[] words = str.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            foreach (string word in words)
            {
                string[] keyValue = word.Split(new char[1] { ':' }, StringSplitOptions.RemoveEmptyEntries);
                if (keyValue[0].Contains("fix") || keyValue[0].Contains("opt"))
                {
                    constValues.Add(keyValue[0] + ":" + keyValue[1]);
                }
            }
            if (constValues.Count != 0)
            {
                foreach (string c in constValues)
                {
                    str = str.Replace(c, "");
                }
            }
            return (str.Contains("X") || str.Contains("Y") || str.Contains("V") || str.Contains("W"));
        }

        //指令变量赋值
        public static CommandData SetVariableData(CommandData commandData, int[] signals)
        {
            foreach (string key in commandData.DataText.Keys)
            {
                try
                {
                    if (!IsContainsVariable(key + ":" + commandData.DataText[key])) continue;
                    commandData.Data[key] = ReplaceVariable(commandData.DataText[key], signals);
                    string[] arstr = ((string)commandData.Data[key]).Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                    if (arstr.Length > 1)
                    {
                        for (int l = 0; l < arstr.Length; l++)
                        {
                            if (IsExpression(arstr[l]))
                            {
                                arstr[l] = ExpressionHandle(arstr[l]);
                            }
                        }
                        commandData.Data[key] = string.Join(",", arstr);
                    }
                    else
                    {
                        if (IsExpression((string)commandData.Data[key]))
                        {
                            commandData.Data[key] = ExpressionHandle((string)commandData.Data[key]);
                        }
                    }
                }
                catch
                {
                }
            }
            commandData.FastSetValue();
            return commandData;
        }

        //字符串表达式处理
        public static string ExpressionHandle(string str)
        {
            string expression = str;
            int index = 0;
            string tempstr = "";
            TStack numStatck = new();
            TStack operStatck = new();
            while (true)
            {
                string ch = expression.Substring(index, 1);
                if (IsNum(ch))
                {
                    tempstr += ch;
                    if (index == expression.Length - 1 || !IsNum(expression.Substring(index + 1, 1)))
                    {
                        numStatck.Push(tempstr);
                    }
                }
                else
                {
                    tempstr = "";
                    if (operStatck.IsEmpty())
                    {
                        operStatck.Push(ch);
                    }
                    else
                    {
                        while (!operStatck.IsEmpty() && GetLevel(ch) <= GetLevel(operStatck.GetTop() + ""))
                        {
                            int res = Calculate(numStatck, operStatck);
                            numStatck.Push(res);
                        }
                        operStatck.Push(ch);
                    }
                }
                index++;
                if (index == expression.Length) break;
            }
            while (!operStatck.IsEmpty())
            {
                int res = Calculate(numStatck, operStatck);
                numStatck.Push(res);
            }
            double dnum = double.Parse(numStatck.Pop() + "");
            int inum = (int)Math.Round(dnum);
            return inum + "";
        }

        //是否为表达式
        public static bool IsExpression(string str)
        {
            Regex regex = new(@"^[0-9\+\-\*\/]+$");
            return !(string.IsNullOrEmpty(regex.Match(str).Value));
        }

        //数值替换变量
        public static string ReplaceVariable(string str, int[] ar)
        {
            char[] vc = ['X', 'Y', 'V', 'W'];
            return str.Replace(vc[0] + "", ar[0] + "").Replace(vc[1] + "", ar[1] + "").Replace(vc[2] + "", ar[2] + "").Replace(vc[3] + "", ar[3] + "");
        }

        //是否为数字
        public static bool IsNum(string ch)
        {
            if (ch == "+" || ch == "-" || ch == "*" || ch == "/") return false;
            return true;
        }

        //符号优先级
        public static int GetLevel(String oper)
        {
            if ("*".Equals(oper) || "/".Equals(oper)) return 1;
            return 0;
        }

        //四则运算
        public static int Calculate(TStack numStatck, TStack operStatck)
        {
            int num1 = 0;
            if (numStatck.GetTop() != null)
            {
                num1 = int.Parse(numStatck.Pop() + "");
            }
            int num2 = 0;
            if (numStatck.GetTop() != null)
            {
                num2 = int.Parse(numStatck.Pop() + "");
            }
            string str = "";
            if (operStatck.GetTop() != null)
            {
                str = operStatck.Pop() + "";
            }
            int res = 0;
            switch (str.Substring(0, 1))
            {
                case "+": res = num1 + num2; break;
                case "-": res = num2 - num1; break;
                case "*": res = num1 * num2; break;
                case "/": res = num2 / num1; break;
            }
            return res;
        }

        //获取玩家眼睛角度
        public static Point2 GetPlayerEyesAngle(ComponentPlayer componentPlayer)
        {
            return DirectionToEyes(GetPlayerEyesDirection(componentPlayer));
            //int vx = (int)(componentPlayer.ComponentBody.Rotation.ToYawPitchRoll().X * 180f / 3.14f) + 180;
            //int vy = (int)(componentPlayer.ComponentLocomotion.LookAngles.Y * 180f / 3.14f) + 90;
            //return new Point2(vx, vy);
        }

        //获取玩家眼睛方向
        public static Vector3 GetPlayerEyesDirection(ComponentPlayer componentPlayer)
        {
            return componentPlayer.ComponentCreatureModel.EyeRotation.GetForwardVector();
        }

        //获取玩家身体方向
        public static Vector3 GetPlayerBodyDirection(ComponentPlayer componentPlayer)
        {
            return componentPlayer.ComponentBody.Matrix.Forward;
        }

        //获取实体所在方块的位置
        public static Point3 GetBodyPoint(ComponentBody componentBody)
        {
            int x = (int)MathF.Floor(componentBody.Position.X);
            int y = (int)MathF.Floor(componentBody.Position.Y);
            int z = (int)MathF.Floor(componentBody.Position.Z);
            return new Point3(x, y, z);
        }

        //由视角获取方向
        public static Vector3 EyesToDirection(Point2 eyes)
        {
            float horizontalAngle = (eyes.X - 180) / 180f * 3.14f;
            float verticalAngle = (eyes.Y - 90) / 180f * 3.14f;
            float y = MathF.Sin(verticalAngle);
            float z = -MathF.Cos(horizontalAngle) * MathF.Cos(verticalAngle);
            float x = -MathF.Sin(horizontalAngle) * MathF.Cos(verticalAngle);
            Vector3 direction = new(x, y, z);
            return direction / direction.Length();
        }

        //由方向获取视角
        public static Point2 DirectionToEyes(Vector3 direction)
        {
            float ay = MathF.Asin(direction.Y / direction.Length());
            float ax = MathF.Acos(direction.Z / direction.XZ.Length());
            int xangle = (int)(ax * 180f / 3.14f) + 1;
            if (direction.X < 0) xangle = 360 - xangle;
            int yangle = (int)(ay * 180f / 3.14f) + 90;
            return new Point2(xangle, yangle);
        }

        //视角相加
        public static Point2 EyesAdd(Point2 eyes1, Point2 eyes2)
        {
            Point2 addEyes = eyes1 + eyes2;
            addEyes.X = addEyes.X % 360;
            if (addEyes.Y > 180) addEyes.Y = 180;
            if (addEyes.Y < 0) addEyes.Y = 0;
            return addEyes;
        }

        //获取命令方块资源路径
        public static string GetCommandPath()
        {
            string path = (Environment.CurrentDirectory == "/") ? "android:SurvivalCraft2.3" : "app:";
            string commandPath = Storage.CombinePaths(path, "Command");
            if (!Storage.DirectoryExists(commandPath)) Storage.CreateDirectory(commandPath);
            return commandPath;
        }

        //获取命令方块资源文件路径
        public static string GetCommandResPathName(string pathName)
        {
            if (GameManager.m_worldInfo != null)
            {
                string worldPath = GameManager.m_worldInfo.DirectoryName;
                if (Storage.FileExists(Storage.CombinePaths(worldPath, pathName)))
                {
                    return Storage.CombinePaths(worldPath, pathName);
                }
            }
            return Storage.CombinePaths(GetCommandPath(), pathName);
        }

        //删除指定文件夹所有文件
        public static void DeleteAllFile(string path)
        {
            foreach (string name in Storage.ListFileNames(path))
            {
                Storage.DeleteFile(Storage.CombinePaths(path, name));
            }
            foreach (string dir in Storage.ListDirectoryNames(path))
            {
                DeleteAllFile(Storage.CombinePaths(path, dir));
            }
        }

        //删除指定文件夹
        public static void DeleteAllDirectory(string path)
        {
            foreach (string dir in Storage.ListDirectoryNames(path))
            {
                DeleteAllDirectory(Storage.CombinePaths(path, dir));
            }
            if (Storage.ListDirectoryNames(path).Count() == 0)
            {
                Storage.DeleteDirectory(path);
            }
        }

        //删除指定路径所有文件包括本身
        public static void DeleteAllDirectoryAndFile(string path)
        {
            DeleteAllFile(path);
            DeleteAllDirectory(path);
        }

        //新的射线投影
        public static object Raycast(Ray3 ray, ComponentMiner componentMiner)
        {
            float reach = 500f;
            Vector3 creaturePosition = componentMiner.ComponentCreature.ComponentCreatureModel.EyePosition;
            Vector3 start = ray.Position;
            var direction = Vector3.Normalize(ray.Direction);
            Vector3 end = ray.Position + direction * 500f;
            Point3 startCell = Terrain.ToCell(start);
            BodyRaycastResult? bodyRaycastResult = componentMiner.m_subsystemBodies.Raycast(start, end, 0.35f, (ComponentBody body, float distance) =>
            {
                bool flag = Vector3.DistanceSquared(start + distance * direction, creaturePosition) <= reach * reach;
                bool flag2 = body.Entity != componentMiner.Entity && !body.IsChildOfBody(componentMiner.ComponentCreature.ComponentBody) && !componentMiner.ComponentCreature.ComponentBody.IsChildOfBody(body);
                bool flag3 = Vector3.Dot(Vector3.Normalize(body.BoundingBox.Center() - start), direction) > 0.7f;
                return (flag && flag2 && flag3) ? true : false;
            });
            MovingBlocksRaycastResult? movingBlocksRaycastResult = componentMiner.m_subsystemMovingBlocks.Raycast(start, end, extendToFillCells: true);
            TerrainRaycastResult? terrainRaycastResult = componentMiner.m_subsystemTerrain.Raycast(start, end, useInteractionBoxes: true, skipAirBlocks: true, delegate (int value, float distance)
            {
                return true;
            });
            float num = bodyRaycastResult.HasValue ? bodyRaycastResult.Value.Distance : float.PositiveInfinity;
            float num2 = movingBlocksRaycastResult.HasValue ? movingBlocksRaycastResult.Value.Distance : float.PositiveInfinity;
            float num3 = terrainRaycastResult.HasValue ? terrainRaycastResult.Value.Distance : float.PositiveInfinity;
            if (num < num2 && num < num3)
            {
                return bodyRaycastResult.Value;
            }
            if (num2 < num && num2 < num3)
            {
                return movingBlocksRaycastResult.Value;
            }
            if (num3 < num && num3 < num2)
            {
                return terrainRaycastResult.Value;
            }
            return new Ray3(start, direction);
        }
    }

    //表达式运算器
    public class ExpressionCalculator
    {
        public class CompositeExpression
        {
            public string Expression;

            public List<string> Includes = new();

            public string[] Separations = new string[3];

            public int Rank;

            public float Result;
        }

        public static string[] Variables = ["x", "y", "z"];

        public static char[] Nums = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];

        public static string[] Funcs = ["sin", "cos", "tan", "ln", "abs", "^", "%"];

        public string Expression;

        public Dictionary<string, CompositeExpression> SubExpressions = new();

        public Dictionary<int, List<string>> Ranks = new();

        public ExpressionCalculator(string expression)
        {
            Expression = expression;
            foreach (string func in Funcs)
            {
                SetFuncExpression(expression, func);
            }
            foreach (var ex1 in SubExpressions.Keys)
            {
                List<string> exs = new();
                foreach (var ex2 in SubExpressions.Keys)
                {
                    if (ex1 != ex2 && ex1.Contains(ex2))
                    {
                        exs.Add(ex2);
                    }
                }
                SubExpressions[ex1].Includes = exs;
            }
            SetExpressionRank();
        }

        public int Calculate(int x, int y, int z)
        {
            return Calculate([x, y, z]);
        }

        public int Calculate(int[] variableValue)
        {
            foreach (int ik in Ranks.Keys)
            {
                foreach (string rex in Ranks[ik])
                {
                    CompositeExpression composite = SubExpressions[rex];
                    string expression2 = composite.Separations[1];
                    foreach (string ci in composite.Includes)
                    {
                        if (float.IsNaN(SubExpressions[ci].Result))
                        {
                            return int.MinValue;
                        }
                        if (SubExpressions[ci].Result < 0)
                        {
                            expression2 = expression2.Replace(ci, "(0" + SubExpressions[ci].Result + ")");
                        }
                        else
                        {
                            expression2 = expression2.Replace(ci, SubExpressions[ci].Result + "");
                        }
                    }
                    string result = CalculateVariableExpression(expression2, Variables, variableValue);
                    float resultTemp = (float)double.Parse(result);
                    string expression3 = string.Empty;
                    float resultTemp2 = 1f;
                    if (composite.Separations[0] == "^" || composite.Separations[0] == "%")
                    {
                        expression3 = composite.Separations[2];
                        foreach (string ci in composite.Includes)
                        {
                            if (SubExpressions[ci].Result < 0)
                            {
                                expression3 = expression3.Replace(ci, "(0" + SubExpressions[ci].Result + ")");
                            }
                            else
                            {
                                expression3 = expression3.Replace(ci, SubExpressions[ci].Result + "");
                            }
                        }
                        result = CalculateVariableExpression(expression3, Variables, variableValue);
                        resultTemp2 = (float)double.Parse(result);
                    }
                    switch (composite.Separations[0])
                    {
                        case "sin": resultTemp = MathF.Sin(resultTemp); break;
                        case "cos": resultTemp = MathF.Cos(resultTemp); break;
                        case "tan": resultTemp = MathF.Tan(resultTemp); break;
                        case "ln": resultTemp = MathF.Log(resultTemp); break;
                        case "abs": resultTemp = MathF.Abs(resultTemp); break;
                        case "%": resultTemp = resultTemp % resultTemp2; break;
                        case "^": resultTemp = MathF.Pow(resultTemp, resultTemp2); break;

                    }
                    composite.Result = resultTemp;
                }
            }
            string expression4 = Expression;
            foreach (string ex in SubExpressions.Keys)
            {
                if (SubExpressions[ex].Result < 0)
                {
                    expression4 = expression4.Replace(ex, "(0" + SubExpressions[ex].Result + ")");
                }
                else
                {
                    expression4 = expression4.Replace(ex, SubExpressions[ex].Result + "");
                }
            }
            string result2 = CalculateVariableExpression(expression4, Variables, variableValue);
            float result3 = (float)double.Parse(result2);
            return (int)MathF.Round(result3);
        }

        //获取子表达式的层级并排序
        public void SetExpressionRank()
        {
            List<string> otsl1 = new();
            List<string> otsl2 = new();
            List<string> otsl3 = new();
            List<string> otsl4 = new();
            List<string> otsl5 = new();
            foreach (string ik in SubExpressions.Keys)
            {
                if (SubExpressions[ik].Includes.Count == 0)
                {
                    otsl1.Add(ik);
                    SubExpressions[ik].Rank = 1;
                }
            }
            foreach (string ik in SubExpressions.Keys)
            {
                if (SubExpressions[ik].Includes.Count > 0)
                {
                    if (otsl1.Contains(ik)) continue;
                    bool allContains = true;
                    foreach (string s in SubExpressions[ik].Includes)
                    {
                        if (!otsl1.Contains(s))
                        {
                            allContains = false;
                            break;
                        }
                    }
                    if (allContains)
                    {
                        otsl2.Add(ik);
                        SubExpressions[ik].Rank = 2;
                    }
                }
            }
            foreach (string ik in SubExpressions.Keys)
            {
                if (SubExpressions[ik].Includes.Count > 1)
                {
                    if (otsl2.Contains(ik) || otsl1.Contains(ik)) continue;
                    bool allContains = true;
                    foreach (string s in SubExpressions[ik].Includes)
                    {
                        if (!otsl2.Contains(s) && !otsl1.Contains(s))
                        {
                            allContains = false;
                            break;
                        }
                    }
                    if (allContains)
                    {
                        otsl3.Add(ik);
                        SubExpressions[ik].Rank = 3;
                    }
                }
            }
            foreach (string ik in SubExpressions.Keys)
            {
                if (SubExpressions[ik].Includes.Count > 2)
                {
                    if (otsl3.Contains(ik) || otsl2.Contains(ik) || otsl1.Contains(ik)) continue;
                    bool allContains = true;
                    foreach (string s in SubExpressions[ik].Includes)
                    {
                        if (!otsl3.Contains(s) && !otsl2.Contains(s) && !otsl1.Contains(s))
                        {
                            allContains = false;
                            break;
                        }
                    }
                    if (allContains)
                    {
                        otsl4.Add(ik);
                        SubExpressions[ik].Rank = 4;
                    }
                }
            }
            foreach (string ik in SubExpressions.Keys)
            {
                if (SubExpressions[ik].Includes.Count > 3)
                {
                    if (otsl4.Contains(ik) || otsl3.Contains(ik) || otsl2.Contains(ik) || otsl1.Contains(ik)) continue;
                    bool allContains = true;
                    foreach (string s in SubExpressions[ik].Includes)
                    {
                        if (!otsl4.Contains(s) && !otsl3.Contains(s) && !otsl2.Contains(s) && !otsl1.Contains(s))
                        {
                            allContains = false;
                            break;
                        }
                    }
                    if (allContains)
                    {
                        otsl5.Add(ik);
                        SubExpressions[ik].Rank = 5;
                    }
                }
            }
            if (otsl1.Count > 0) Ranks[1] = otsl1;
            if (otsl2.Count > 0) Ranks[2] = otsl2;
            if (otsl3.Count > 0) Ranks[3] = otsl3;
            if (otsl4.Count > 0) Ranks[4] = otsl4;
            if (otsl5.Count > 0) Ranks[5] = otsl5;

            string[] sortsSubExpressions = new string[SubExpressions.Count];
            int ii = 0;
            foreach (string ex in SubExpressions.Keys)
            {
                sortsSubExpressions[ii++] = ex;
            }
            for (int i = 0; i < sortsSubExpressions.Length - 1; i++)
            {
                for (int j = 0; j < sortsSubExpressions.Length - 1 - i; j++)
                {
                    if (SubExpressions[sortsSubExpressions[j]].Rank < SubExpressions[sortsSubExpressions[j + 1]].Rank)
                    {
                        var temp = sortsSubExpressions[j + 1];
                        sortsSubExpressions[j + 1] = sortsSubExpressions[j];
                        sortsSubExpressions[j] = temp;
                    }
                }
            }
            Dictionary<string, CompositeExpression> tempSubExpressions = new();
            foreach (string sex in sortsSubExpressions)
            {
                tempSubExpressions[sex] = SubExpressions[sex];
            }
            SubExpressions = tempSubExpressions;
            foreach (var ex in SubExpressions.Keys)
            {
                string[] tempIncludes = SubExpressions[ex].Includes.ToArray();
                for (int i = 0; i < tempIncludes.Length - 1; i++)
                {
                    for (int j = 0; j < tempIncludes.Length - 1 - i; j++)
                    {
                        if (SubExpressions[tempIncludes[j]].Rank < SubExpressions[tempIncludes[j + 1]].Rank)
                        {
                            var temp = tempIncludes[j + 1];
                            tempIncludes[j + 1] = tempIncludes[j];
                            tempIncludes[j] = temp;
                        }
                    }
                }
                SubExpressions[ex].Includes.Clear();
                foreach (string ti in tempIncludes)
                {
                    SubExpressions[ex].Includes.Add(ti);
                }
            }
        }

        //获取复杂函数的子表达式
        public void SetFuncExpression(string expression, string func)
        {
            if (expression.Contains(func))
            {
                List<int> indexs = new();
                int start = expression.IndexOf(func);
                while (start != -1)
                {
                    indexs.Add(start);
                    start = expression.IndexOf(func, start + 1);
                }
                if (func == "^" || func == "%")
                {
                    foreach (int index in indexs)
                    {
                        string leftExpression = string.Empty;
                        string rightExpression = string.Empty;
                        if (expression[index - 1] == ')')
                        {
                            int leftBracket = 0;
                            int rightBracket = 0;
                            for (int i = index; i >= 0; i--)
                            {
                                if (expression[i] == '(') leftBracket++;
                                if (expression[i] == ')') rightBracket++;
                                if (leftBracket == rightBracket && leftBracket != 0)
                                {
                                    leftExpression = expression.Substring(i, index - i);
                                    break;
                                }
                            }
                        }
                        else
                        {
                            for (int i = index - 1; i >= 0; i--)
                            {
                                if (!IsNum(expression[i]) && expression[i] != '.' && !IsVariables(expression[i], Variables))
                                    break;
                                leftExpression = expression[i] + leftExpression;
                            }
                        }
                        if (expression[index + 1] == '(')
                        {
                            int leftBracket = 0;
                            int rightBracket = 0;
                            for (int i = index; i < expression.Length; i++)
                            {
                                if (expression[i] == '(') leftBracket++;
                                if (expression[i] == ')') rightBracket++;
                                if (leftBracket == rightBracket && leftBracket != 0)
                                {
                                    rightExpression = expression.Substring(index + 1, i - index);
                                    break;
                                }
                            }
                        }
                        else
                        {
                            for (int i = index + 1; i < expression.Length; i++)
                            {
                                if (!IsNum(expression[i]) && expression[i] != '.' && !IsVariables(expression[i], Variables))
                                    break;
                                rightExpression = rightExpression + expression[i];
                            }
                        }
                        CompositeExpression composite = new();
                        composite.Expression = leftExpression + func + rightExpression;
                        composite.Separations[0] = func;
                        composite.Separations[1] = leftExpression;
                        composite.Separations[2] = rightExpression;
                        SubExpressions[composite.Expression] = composite;
                    }
                }
                else
                {
                    foreach (int index in indexs)
                    {
                        int leftBracket = 0;
                        int rightBracket = 0;
                        for (int i = index; i < expression.Length; i++)
                        {
                            if (expression[i] == '(') leftBracket++;
                            if (expression[i] == ')') rightBracket++;
                            if (leftBracket == rightBracket && leftBracket != 0)
                            {
                                string subExpression = expression.Substring(index, i - index + 1);
                                CompositeExpression composite = new();
                                composite.Expression = subExpression;
                                composite.Separations[0] = func;
                                composite.Separations[1] = composite.Expression.Substring(composite.Separations[0].Length + 1, composite.Expression.Length - composite.Separations[0].Length - 2);
                                SubExpressions[subExpression] = composite;
                                break;
                            }
                        }
                    }
                }
            }
        }

        //数值替换变量并计算
        public string CalculateVariableExpression(string expression, string[] variableChar, int[] variableValue)
        {
            if (variableChar.Length != variableValue.Length) return expression;
            if (expression.Contains("NaN")) return "NaN";
            for (int i = 0; i < variableChar.Length; i++)
            {
                if (variableValue[i] < 0)
                {
                    expression = expression.Replace(variableChar[i], "(0" + variableValue[i] + ")");
                }
                else
                {
                    expression = expression.Replace(variableChar[i], variableValue[i] + "");
                }
            }
            expression = Standardize(expression);
            if (IsNum(expression)) return expression;
            string epostfixExpression = GetEpostfixExpression(expression);
            string result = CalculateEpostfixExpression(epostfixExpression);
            return result;
        }

        //是否为数字
        public bool IsNum(char c)
        {
            foreach (char n in Nums)
            {
                if (c == n) return true;
            }
            return false;
        }

        //是否为数值
        public bool IsNum(string expression)
        {
            if (expression.StartsWith("-"))
            {
                expression = expression.Substring(1);
            }
            Regex regex = new(@"^[0-9\.]+$");
            return !(string.IsNullOrEmpty(regex.Match(expression).Value));
        }

        //是否为变量
        public bool IsVariables(char c, string[] variables)
        {
            foreach (string v in variables)
            {
                if (char.Parse(v) == c) return true;
            }
            return false;
        }

        //是否为表达式
        public bool IsExpression(string expression)
        {
            Regex regex = new(@"^[0-9\+\-\*\/\(\)\.]+$");
            return !(string.IsNullOrEmpty(regex.Match(expression).Value));
        }

        //表达式标准化
        public string Standardize(string expression)
        {
            expression = expression.Replace(" ", "");
            expression = expression.Replace("(-", "(0-");
            expression = expression.Replace("pi", MathF.PI.ToString());
            expression = expression.Replace("e", MathF.E.ToString());
            return expression;
        }

        //中缀转换成后缀表达式
        public string GetEpostfixExpression(string expression)
        {
            List<string> operatorList = new();
            string operatorStr;
            string epostfixExpression = "";
            string operandStr;
            while (expression.Length > 0)
            {
                operandStr = "";

                if (IsNum(expression[0]))
                {
                    while (IsNum(expression[0]) || expression[0] == '.')
                    {
                        operandStr += expression[0].ToString();
                        expression = expression.Substring(1);
                        if (expression == "") break;
                    }
                    epostfixExpression += operandStr + "|";
                }

                if (expression.Length > 0 && expression[0].ToString() == "(")
                {
                    operatorList.Add("(");
                    expression = expression.Substring(1);
                }

                operandStr = "";
                if (expression.Length > 0 && expression[0].ToString() == ")")
                {
                    do
                    {
                        if (operatorList[operatorList.Count - 1].ToString() != "(")
                        {
                            operandStr += operatorList[operatorList.Count - 1].ToString() + "|";
                            operatorList.RemoveAt(operatorList.Count - 1);
                        }
                        else
                        {
                            operatorList.RemoveAt(operatorList.Count - 1);
                            break;
                        }

                    }
                    while (true);
                    epostfixExpression += operandStr;
                    expression = expression.Substring(1);
                }

                operandStr = "";
                if (expression.Length > 0 && (expression[0].ToString() == "*" || expression[0].ToString() == "/" || expression[0].ToString() == "+" || expression[0].ToString() == "-"))
                {
                    operatorStr = expression[0].ToString();
                    if (operatorList.Count > 0)
                    {
                        if (operatorList[operatorList.Count - 1].ToString() == "(" || OperatorPriority(operatorStr, operatorList[operatorList.Count - 1].ToString()))
                        {
                            operatorList.Add(operatorStr);
                        }
                        else
                        {
                            operandStr += operatorList[operatorList.Count - 1].ToString() + "|";
                            operatorList.RemoveAt(operatorList.Count - 1);
                            operatorList.Add(operatorStr);
                            epostfixExpression += operandStr;
                        }
                    }
                    else
                    {
                        operatorList.Add(operatorStr);
                    }
                    expression = expression.Substring(1);
                }
            }
            operandStr = "";
            while (operatorList.Count != 0)
            {
                operandStr += operatorList[operatorList.Count - 1].ToString() + "|";
                operatorList.RemoveAt(operatorList.Count - 1);
            }
            if (operandStr.Length > 0)
            {
                epostfixExpression += operandStr.Substring(0, operandStr.Length - 1);
            }
            return epostfixExpression;
        }

        //计算后缀表达式
        public string CalculateEpostfixExpression(string epostfixExpression)
        {
            List<string> operandlist2 = new();
            float operand1;
            float operand2;
            string[] operandAr;
            epostfixExpression = epostfixExpression.Replace(" ", "");
            operandAr = epostfixExpression.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
            for (int i = 0; i < operandAr.Length; i++)
            {
                if (IsNum(operandAr[i][0]))
                {
                    operandlist2.Add(operandAr[i].ToString());
                }
                else
                {
                    operand2 = (float)double.Parse(operandlist2[operandlist2.Count - 1]);
                    operandlist2.RemoveAt(operandlist2.Count - 1);
                    operand1 = (float)double.Parse(operandlist2[operandlist2.Count - 1]);
                    operandlist2.RemoveAt(operandlist2.Count - 1);
                    operandlist2.Add(Arithmetic(operand1, operand2, operandAr[i]).ToString());
                }
            }
            return operandlist2[0].ToString();
        }

        //判断运算符优先级
        public bool OperatorPriority(string operator1, string operator2)
        {
            if (operator1 == "*" && operator2 == "+") return true;
            else if (operator1 == "*" && operator2 == "-") return true;
            else if (operator1 == "/" && operator2 == "+") return true;
            else if (operator1 == "/" && operator2 == "-") return true;
            else return false;
        }

        //四则运算
        public float Arithmetic(float operand1, float operand2, string str_operator)
        {
            switch (str_operator)
            {
                case "*": operand1 *= operand2; break;
                case "/": operand1 /= operand2; break;
                case "+": operand1 += operand2; break;
                case "-": operand1 -= operand2; break;
                default: break;
            }
            return operand1;
        }
    }

    //立方区域
    public class CubeArea
    {
        public Point3 MaxPoint;
        public Point3 MinPoint;
        public Point3 Current;
        public Point3 Center;
        public int LengthX;
        public int LengthY;
        public int LengthZ;
        public bool IsPoint;

        public CubeArea(Point3 point1, Point3 point2)
        {
            MaxPoint = new Point3(Math.Max(point1.X, point2.X), Math.Max(point1.Y, point2.Y), MathUtils.Max(point1.Z, point2.Z));
            MinPoint = new Point3(Math.Min(point1.X, point2.X), Math.Min(point1.Y, point2.Y), MathUtils.Min(point1.Z, point2.Z));
            Current = MinPoint;
            LengthX = Math.Abs(point1.X - point2.X) + 1;
            LengthY = Math.Abs(point1.Y - point2.Y) + 1;
            LengthZ = Math.Abs(point1.Z - point2.Z) + 1;
            Center = MinPoint + new Point3((int)(LengthX / 2f), (int)(LengthY / 2f), (int)(LengthZ / 2f));
            IsPoint = (LengthX == 1 && LengthY == 1 && LengthZ == 1);
        }

        public bool Ergodic(Func<bool> action)
        {
            for (int x = 0; x < LengthX; x++)
            {
                for (int y = 0; y < LengthY; y++)
                {
                    for (int z = 0; z < LengthZ; z++)
                    {
                        Current = new Point3(x + MinPoint.X, y + MinPoint.Y, z + MinPoint.Z);
                        bool result = action.Invoke();
                        if (result) return true;
                    }
                }
            }
            return false;
        }

        public void Ergodic(int division, float time, Action<Point3, Point3, Point3> action)
        {
            int chunkXL = (int)(LengthX / division) + 1;
            int chunkYL = (int)(LengthY / division) + 1;
            int chunkZL = (int)(LengthZ / division) + 1;
            float t = 0f;
            List<Point3> points = new();
            int p = 0;
            for (int ci = 0; ci < chunkXL; ci++)
            {
                for (int cj = 0; cj < chunkYL; cj++)
                {
                    for (int ck = 0; ck < chunkZL; ck++)
                    {
                        points.Add(new Point3(ci, cj, ck));
                        Time.QueueTimeDelayedExecution(Time.RealTime + t, delegate
                        {
                            int ox = MinPoint.X + points[p].X * division;
                            int oy = MinPoint.Y + points[p].Y * division;
                            int oz = MinPoint.Z + points[p].Z * division;
                            action.Invoke(new Point3(ox, oy, oz), points[p], points[points.Count - 1]);
                            p++;
                        });
                        t += time;
                    }
                }
            }
        }

        public void ErgodicByChunk(float perpareTime, float intervalTime, Action<Point3, Point2, Point2> action)
        {
            int chunkXL = (int)(LengthX / 16) + 1;
            int chunkZL = (int)(LengthZ / 16) + 1;
            action.Invoke(MinPoint, new Point2(-1, -1), new Point2(-1, -1));
            float time = perpareTime;
            int p = 0;
            List<Point2> points = new();
            for (int ci = 0; ci < chunkXL; ci++)
            {
                for (int cj = 0; cj < chunkZL; cj++)
                {
                    points.Add(new Point2(ci, cj));
                    Time.QueueTimeDelayedExecution(Time.RealTime + time, delegate
                    {
                        int ox = MinPoint.X + points[p].X * 16;
                        int oy = MinPoint.Y;
                        int oz = MinPoint.Z + points[p].Y * 16;
                        action.Invoke(new Point3(ox, oy, oz), points[p], points[points.Count - 1]);
                        p++;
                    });
                    time += intervalTime;
                }
            }
        }

        public bool Exist(Vector3 target)
        {
            bool pass1 = (target.X >= MinPoint.X && target.Y >= MinPoint.Y && target.Z >= MinPoint.Z);
            bool pass2 = (target.X <= MaxPoint.X + 1 && target.Y <= MaxPoint.Y + 1 && target.Z <= MaxPoint.Z + 1);
            return pass1 && pass2;
        }

        public bool Exist(Point3 target)
        {
            bool pass1 = (target.X >= MinPoint.X && target.Y >= MinPoint.Y && target.Z >= MinPoint.Z);
            bool pass2 = (target.X <= MaxPoint.X && target.Y <= MaxPoint.Y && target.Z <= MaxPoint.Z);
            return pass1 && pass2;
        }
    }

    //圆球区域
    public class SphereArea
    {
        public int Radius;
        public int SquaRadius;
        public Point3 Center;
        public Point3 Current;
        public Point3 MaxPoint;
        public Point3 MinPoint;

        public SphereArea(int r, Point3 c)
        {
            Radius = r;
            SquaRadius = r * r;
            Center = c;
            Current = Center;
            MaxPoint = Center + new Point3(Radius);
            MinPoint = Center - new Point3(Radius);
        }

        public void Ergodic(Action action)
        {
            for (int x = -Radius + 1; x < Radius; x++)
            {
                for (int y = -Radius + 1; y < Radius; y++)
                {
                    for (int z = -Radius + 1; z < Radius; z++)
                    {
                        if (x * x + y * y + z * z <= SquaRadius)
                        {
                            Current = new Point3(Center.X + x, Center.Y + y, Center.Z + z);
                            action.Invoke();
                        }
                    }
                }
            }
        }

        public bool Exist(Point3 target)
        {
            Point3 p = target - Center;
            if (p.X * p.X == SquaRadius || p.Y * p.Y == SquaRadius || p.Z * p.Z == SquaRadius)
            {
                return false;
            }
            return (p.X * p.X + p.Y * p.Y + p.Z * p.Z <= SquaRadius);
        }
    }

    //圆柱区域
    public class ColumnArea
    {
        public int Radius;
        public int Height;
        public Point3 Center;
        public CoordDirection Coord;
        public Point3 MaxPoint;
        public Point3 MinPoint;
        public Point3 Current;
        public int LengthX;
        public int LengthY;
        public int LengthZ;

        public ColumnArea(int r, int h, Point3 c, CoordDirection coord)
        {
            Radius = r;
            Height = h;
            Center = c;
            Coord = coord;
            Current = Center;
            switch (Coord)
            {
                case CoordDirection.PX:
                    {
                        MinPoint = Center - new Point3(0, Radius, Radius);
                        MaxPoint = Center + new Point3(Height, Radius, Radius);
                    }
                    break;
                case CoordDirection.PY:
                    {
                        MinPoint = Center - new Point3(Radius, 0, Radius);
                        MaxPoint = Center + new Point3(Radius, Height, Radius);
                    }
                    break;
                case CoordDirection.PZ:
                    {
                        MinPoint = Center - new Point3(Radius, Radius, 0);
                        MaxPoint = Center + new Point3(Radius, Radius, Height);
                    }
                    break;
                case CoordDirection.NX:
                    {
                        MinPoint = Center - new Point3(Height, Radius, Radius);
                        MaxPoint = Center + new Point3(0, Radius, Radius);
                    }
                    break;
                case CoordDirection.NY:
                    {
                        MinPoint = Center - new Point3(Radius, Height, Radius);
                        MaxPoint = Center + new Point3(Radius, 0, Radius);
                    }
                    break;
                case CoordDirection.NZ:
                    {
                        MinPoint = Center - new Point3(Radius, Radius, Height);
                        MaxPoint = Center + new Point3(Radius, Radius, 0);
                    }
                    break;
            }
            LengthX = MaxPoint.X - MinPoint.X + 1;
            LengthY = MaxPoint.Y - MinPoint.Y + 1;
            LengthZ = MaxPoint.Z - MinPoint.Z + 1;
        }

        public void Ergodic(Action action)
        {
            for (int x = 0; x < LengthX; x++)
            {
                for (int y = 0; y < LengthY; y++)
                {
                    for (int z = 0; z < LengthZ; z++)
                    {
                        Current = new Point3(x + MinPoint.X, y + MinPoint.Y, z + MinPoint.Z);
                        int dx = Current.X - Center.X;
                        int dy = Current.Y - Center.Y;
                        int dz = Current.Z - Center.Z;
                        int sr = 0;
                        switch (Coord)
                        {
                            case CoordDirection.PX: sr = dy * dy + dz * dz; break;
                            case CoordDirection.NX: sr = dy * dy + dz * dz; break;
                            case CoordDirection.PY: sr = dx * dx + dz * dz; break;
                            case CoordDirection.NY: sr = dx * dx + dz * dz; break;
                            case CoordDirection.PZ: sr = dx * dx + dy * dy; break;
                            case CoordDirection.NZ: sr = dx * dx + dy * dy; break;
                        }
                        if (sr <= Radius * Radius)
                        {
                            action.Invoke();
                        }
                    }
                }
            }
        }

        public bool Exist(Point3 target)
        {
            bool pass = (target.X >= MinPoint.X && target.Y >= MinPoint.Y && target.Z >= MinPoint.Z);
            bool pass2 = (target.X <= MaxPoint.X && target.Y <= MaxPoint.Y && target.Z <= MaxPoint.Z);
            if (!(pass && pass2)) return false;
            Point3 p = target - Center;
            int sr = 0;
            switch (Coord)
            {
                case CoordDirection.PX: sr = p.Y * p.Y + p.Z * p.Z; break;
                case CoordDirection.NX: sr = p.Y * p.Y + p.Z * p.Z; break;
                case CoordDirection.PY: sr = p.X * p.X + p.Z * p.Z; break;
                case CoordDirection.NY: sr = p.X * p.X + p.Z * p.Z; break;
                case CoordDirection.PZ: sr = p.X * p.X + p.Y * p.Y; break;
                case CoordDirection.NZ: sr = p.X * p.X + p.Y * p.Y; break;
            }
            return (sr <= Radius * Radius);
        }
    }

    //圆锥区域
    public class ConeArea
    {
        public int Radius;
        public int Height;
        public Point3 Center;
        public CoordDirection Coord;
        public Point3 MaxPoint;
        public Point3 MinPoint;
        public Point3 Current;
        public int LengthX;
        public int LengthY;
        public int LengthZ;

        public ConeArea(int r, int h, Point3 c, CoordDirection coord)
        {
            Radius = r;
            Height = h;
            Center = c;
            Coord = coord;
            Current = Center;
            switch (Coord)
            {
                case CoordDirection.PX:
                    {
                        MinPoint = Center - new Point3(0, Radius, Radius);
                        MaxPoint = Center + new Point3(Height, Radius, Radius);
                    }
                    break;
                case CoordDirection.PY:
                    {
                        MinPoint = Center - new Point3(Radius, 0, Radius);
                        MaxPoint = Center + new Point3(Radius, Height, Radius);
                    }
                    break;
                case CoordDirection.PZ:
                    {
                        MinPoint = Center - new Point3(Radius, Radius, 0);
                        MaxPoint = Center + new Point3(Radius, Radius, Height);
                    }
                    break;
                case CoordDirection.NX:
                    {
                        MinPoint = Center - new Point3(Height, Radius, Radius);
                        MaxPoint = Center + new Point3(0, Radius, Radius);
                    }
                    break;
                case CoordDirection.NY:
                    {
                        MinPoint = Center - new Point3(Radius, Height, Radius);
                        MaxPoint = Center + new Point3(Radius, 0, Radius);
                    }
                    break;
                case CoordDirection.NZ:
                    {
                        MinPoint = Center - new Point3(Radius, Radius, Height);
                        MaxPoint = Center + new Point3(Radius, Radius, 0);
                    }
                    break;
            }
            LengthX = MaxPoint.X - MinPoint.X + 1;
            LengthY = MaxPoint.Y - MinPoint.Y + 1;
            LengthZ = MaxPoint.Z - MinPoint.Z + 1;
        }

        public int GetSectionRadius(int d)
        {
            return (int)MathF.Pow((Height - MathF.Abs(d)) * Radius / Height, 2f);
        }

        public void Ergodic(Action action)
        {
            for (int x = 0; x < LengthX; x++)
            {
                for (int y = 0; y < LengthY; y++)
                {
                    for (int z = 0; z < LengthZ; z++)
                    {
                        Current = new Point3(x + MinPoint.X, y + MinPoint.Y, z + MinPoint.Z);
                        int dx = Current.X - Center.X;
                        int dy = Current.Y - Center.Y;
                        int dz = Current.Z - Center.Z;
                        int sr = 0;
                        int nr = 0;
                        switch (Coord)
                        {
                            case CoordDirection.PX: sr = dy * dy + dz * dz; nr = GetSectionRadius(dx); break;
                            case CoordDirection.NX: sr = dy * dy + dz * dz; nr = GetSectionRadius(dx); break;
                            case CoordDirection.PY: sr = dx * dx + dz * dz; nr = GetSectionRadius(dy); break;
                            case CoordDirection.NY: sr = dx * dx + dz * dz; nr = GetSectionRadius(dy); break;
                            case CoordDirection.PZ: sr = dx * dx + dy * dy; nr = GetSectionRadius(dz); break;
                            case CoordDirection.NZ: sr = dx * dx + dy * dy; nr = GetSectionRadius(dz); break;
                        }
                        if (sr <= nr)
                        {
                            action.Invoke();
                        }
                    }
                }
            }
        }

        public bool Exist(Point3 target)
        {
            bool pass = (target.X >= MinPoint.X && target.Y >= MinPoint.Y && target.Z >= MinPoint.Z);
            bool pass2 = (target.X <= MaxPoint.X && target.Y <= MaxPoint.Y && target.Z <= MaxPoint.Z);
            if (!(pass && pass2)) return false;
            Point3 p = target - Center;
            int sr = 0;
            int nr = 0;
            switch (Coord)
            {
                case CoordDirection.PX: sr = p.Y * p.Y + p.Z * p.Z; nr = GetSectionRadius(p.X); break;
                case CoordDirection.NX: sr = p.Y * p.Y + p.Z * p.Z; nr = GetSectionRadius(p.X); break;
                case CoordDirection.PY: sr = p.X * p.X + p.Z * p.Z; nr = GetSectionRadius(p.Y); break;
                case CoordDirection.NY: sr = p.X * p.X + p.Z * p.Z; nr = GetSectionRadius(p.Y); break;
                case CoordDirection.PZ: sr = p.X * p.X + p.Y * p.Y; nr = GetSectionRadius(p.Z); break;
                case CoordDirection.NZ: sr = p.X * p.X + p.Y * p.Y; nr = GetSectionRadius(p.Z); break;
            }
            return (sr <= nr);
        }
    }

    //栈
    public class TStack
    {
        public int top = -1;

        public int maxSize = 64;

        public Object[] array = new Object[64];

        public void Push(Object val)
        {
            if (top == maxSize - 1) return;
            array[++top] = val;
        }

        public Object Pop()
        {
            if (top == -1) return null;
            return array[top--];
        }

        public Object GetTop()
        {
            if (top == -1) return null;
            return array[top];
        }

        public bool IsEmpty()
        {
            if (top == -1) return true;
            return false;
        }
    }
}

namespace LibPixz2
{
    public struct ComponentInfo
    {
        public byte id;
        public byte samplingFactorX;
        public byte samplingFactorY;
        public byte quantTableId;
        public byte dcHuffmanTable;
        public byte acHuffmanTable;
    }

    public struct QuantTable
    {
        public bool valid;
        public byte id;
        public ushort length;
        public byte precision;
        public ushort[] table;
    }

    public struct HuffmanTable
    {
        public bool valid;
        public byte id;
        public byte type;
        public byte[] numSymbols;
        public byte[] codes;
        public byte maxCodeLength;
        public List<Huffman.CodeInfo> table;
        public Huffman.CodeInfo[] preIndexTable;
    }

    public struct Info
    {
        public float a;
        public float b;
        public float c;
    }

    public struct Color2
    {
        public byte a;
        public byte r;
        public byte g;
        public byte b;
    }

    public enum Markers
    {
        LiteralFF = 0x00,
        Soi = 0xd8,
        App0 = 0xe0,
        App14 = 0xee,
        Dqt = 0xdb,
        Sof0 = 0xc0, Sof2 = 0xc2,
        Dht = 0xc4,
        Rs0 = 0xd0, Rs1 = 0xd1, Rs2 = 0xd2, Rs3 = 0xd3,
        Rs4 = 0xd4, Rs5 = 0xd5, Rs6 = 0xd6, Rs7 = 0xd7,
        Sos = 0xda,
        Eoi = 0xd9,
        Dri = 0xdd
    }

    public enum App14ColorMode
    {
        Unknown,
        YCbCr,
        YCCK
    }

    public interface IColorspaceConverter
    {
        Color2 ConvertToRgb(Info info);
        Info ConvertFromRgb(Color2 rgb);
    }

    public class ImgInfo
    {
        public ushort length;
        public byte dataPrecision;
        public ushort height;
        public ushort width;
        public bool hasRestartMarkers;
        public ushort restartInterval;
        public byte numOfComponents;
        public ComponentInfo[] components;
        public HuffmanTable[,] huffmanTables = new HuffmanTable[2, 4];
        public QuantTable[] quantTables = new QuantTable[4];
        public bool startOfImageFound;
        // For App14 Marker (Adobe)
        public bool app14MarkerFound;
        public App14ColorMode colorMode;
        // Helper image decoding variables
        public short[] deltaDc;
        //public short restartMarker;
        public int mcuStrip = 0;
        public Markers prevRestMarker = Markers.Rs7;
        // Constants
        public const int blockSize = 8;
    }

    public class Pixz
    {
        public static DynamicArray<Image> Decode(Stream stream)
        {
            var reader = new BinaryReader(stream);
            var images = new DynamicArray<Image>();
            stream.Seek(0, SeekOrigin.Begin);
            var imgInfo = new ImgInfo();
            for (long length = stream.Length; ;)
            {
                int markerId;
                do
                {
                    if (stream.Position == length)
                        goto end;
                } while (reader.ReadByte() != 0xff);
                markerId = reader.ReadByte();
                Markers markers = (Markers)markerId;
                switch (markers)
                {
                    case Markers.App0: ReadForApp0(reader, imgInfo); break;
                    case Markers.App14: ReadForApp14(reader, imgInfo); break;
                    case Markers.Dqt: ReadForDqt(reader, imgInfo); break;
                    case Markers.Sof0: ReadForSof0(reader, imgInfo); break;
                    case Markers.Dht: ReadForDht(reader, imgInfo); break;
                    case Markers.Dri: ReadForDri(reader, imgInfo); break;
                    case Markers.Sos: images.Add(ReadForSos(reader, imgInfo)); break;
                    case Markers.Soi: imgInfo = new ImgInfo { startOfImageFound = true }; break;
                    case Markers.Eoi: break;
                    case Markers.Sof2: break;
                    default: ReadForMarker(reader, imgInfo, (Markers)markerId); break;
                }
            }
        end:
            reader.Dispose();
            return images;
        }

        public static void ReadForApp0(BinaryReader reader, ImgInfo imgInfo)
        {
            ushort length = reader.ReadUInt16();
            reader.BaseStream.Seek(length - 2, SeekOrigin.Current);
        }

        public static void ReadForApp14(BinaryReader reader, ImgInfo imgInfo)
        {
            reader.ReadUInt16();
            reader.ReadBytes(11);
            imgInfo.app14MarkerFound = true;
            imgInfo.colorMode = (App14ColorMode)reader.ReadByte();
            if ((int)imgInfo.colorMode > 2) throw new Exception("Invalid Adobe colorspace");

        }

        public static void ReadForDqt(BinaryReader reader, ImgInfo imgInfo)
        {
            int markerLength = reader.ReadUInt16() - 2;
            while (markerLength > 0)
            {
                int length = ReadDqtTable(reader, imgInfo);
                markerLength -= length;
            }
        }

        public static void ReadForSof0(BinaryReader reader, ImgInfo imgInfo)
        {
            imgInfo.length = reader.ReadUInt16();
            imgInfo.dataPrecision = reader.ReadByte();
            imgInfo.height = reader.ReadUInt16();
            imgInfo.width = reader.ReadUInt16();
            imgInfo.numOfComponents = reader.ReadByte();
            if (imgInfo.length < 8) throw new Exception("Invalid length of Sof0");
            if (imgInfo.height == 0 || imgInfo.width == 0) throw new Exception("Invalid image size");
            if (imgInfo.dataPrecision != 8) throw new Exception("Unsupported data precision");
            if (imgInfo.numOfComponents != 1 && imgInfo.numOfComponents != 3) throw new Exception("Invalid number of components");
            imgInfo.components = new ComponentInfo[imgInfo.numOfComponents];
            for (int i = 0; i < imgInfo.numOfComponents; i++)
            {
                byte id = reader.ReadByte();
                if (id > 3) throw new Exception("Invalid component type");
                byte samplingFactor = reader.ReadByte();
                imgInfo.components[i].id = id;
                imgInfo.components[i].samplingFactorX = (byte)(samplingFactor >> 4);
                imgInfo.components[i].samplingFactorY = (byte)(samplingFactor & 0x0f);
                imgInfo.components[i].quantTableId = reader.ReadByte();
            }

        }

        public static void ReadForDht(BinaryReader reader, ImgInfo imgInfo)
        {
            int markerLength = reader.ReadUInt16() - 2;
            while (markerLength > 0)
            {
                int length = ReadDhtTable(reader, imgInfo);
                markerLength -= length;
            }
        }

        public static void ReadForDri(BinaryReader reader, ImgInfo imgInfo)
        {
            reader.ReadUInt16();
            ushort restartInterval = reader.ReadUInt16();
            if (restartInterval == 0) throw new Exception("Invalid restart interval (0)");
            imgInfo.restartInterval = restartInterval;
            imgInfo.hasRestartMarkers = true;
        }

        public static Image ReadForSos(BinaryReader reader, ImgInfo imgInfo)
        {
            if (imgInfo.numOfComponents != 1 && imgInfo.numOfComponents != 3)
                throw new Exception("Unsupported number of components (" +
                    imgInfo.numOfComponents + ")");
            ushort length = reader.ReadUInt16();
            byte componentsInScan = reader.ReadByte();
            for (int i = 0; i < componentsInScan; i++)
            {
                byte componentId = (byte)(reader.ReadByte() - 1);
                byte huffmanTables = reader.ReadByte();
                byte acTable = (byte)(huffmanTables & 0xf);
                byte dcTable = (byte)(huffmanTables >> 4);
                imgInfo.components[componentId].dcHuffmanTable = dcTable;
                imgInfo.components[componentId].acHuffmanTable = acTable;
            }
            reader.ReadBytes(3); // "Unused" bytes
            return ImageDecoder.DecodeImage(reader, imgInfo);
        }

        public static void ReadForMarker(BinaryReader reader, ImgInfo imgInfo, Markers markerId)
        {
            // Check if marker is not followed by a length argument
            if (markerId >= Markers.Rs0 && markerId <= Markers.Rs7)
                return;
            if (markerId == Markers.LiteralFF)
                return;
            if (!imgInfo.startOfImageFound) return;
            ushort length = reader.ReadUInt16();
            reader.BaseStream.Seek(length - 2, SeekOrigin.Current);
        }

        public static int ReadDqtTable(BinaryReader reader, ImgInfo imgInfo)
        {
            byte tableInfo = reader.ReadByte();
            byte tableId = (byte)(tableInfo & 0xf); // Low 4 bits of tableInfo
            if (tableId > 3) throw new Exception("Invalid ID for quantization table");
            var quantTable = new QuantTable
            {
                id = tableId,
                precision = (byte)(tableInfo >> 4), // High 4 bits of tableInfo
                valid = true,
                table = new ushort[64]
            };
            int sizeOfElement = quantTable.precision == 0 ? 1 : 2;
            var tabla = FileOps.tablasZigzag[8];
            // Quantizer tables are in zigzag too!
            if (quantTable.precision == 0)
            {
                for (int i = 0; i < 64; i++)
                {
                    quantTable.table[tabla[i].Y * 8 + tabla[i].X] = reader.ReadByte();
                }
            }
            else
            {
                for (int i = 0; i < 64; i++)
                {
                    quantTable.table[tabla[i].Y * 8 + tabla[i].X] = reader.ReadUInt16();
                }
            }
            imgInfo.quantTables[tableId] = quantTable;
            return 1 + 64 * sizeOfElement;
        }

        public static int ReadDhtTable(BinaryReader reader, ImgInfo imgInfo)
        {
            byte tableInfo = reader.ReadByte();
            byte tableId = (byte)(tableInfo & 0x7); // Low 3 bits of tableInfo
            int numCodes = 0;
            if (tableId > 3) throw new Exception("Invalid ID for huffman table");
            if ((tableInfo & 0xe0) != 0)  // High 3 bits of tableInfo must be zero
                throw new Exception("Invalid huffman table");
            var huffmanTable = new HuffmanTable
            {
                id = tableId,
                type = (byte)((tableInfo >> 4) & 0x1), // Bit 4 of tableInfo
                valid = true,
                numSymbols = new byte[16]
            };
            for (int i = 0; i < 16; i++)
            {
                huffmanTable.numSymbols[i] = reader.ReadByte();
                numCodes += huffmanTable.numSymbols[i];
            }
            huffmanTable.codes = new byte[numCodes];
            for (int i = 0; i < numCodes; i++)
            {
                huffmanTable.codes[i] = reader.ReadByte();
            }
            Huffman.CreateTable(ref huffmanTable);
            imgInfo.huffmanTables[huffmanTable.type, huffmanTable.id] = huffmanTable;
            return 1 + 16 + numCodes;
        }
    }

    public class FileOps
    {
        public static Dictionary<int, Point2[]> tablasZigzag = new()
        {
             { 8, GetZigzagTable(8, 8) },
             { 16, GetZigzagTable(16, 16) },
             { 32, GetZigzagTable(32, 32) },
             { 64, GetZigzagTable(64, 64) }
        };

        public static Point2[] GetZigzagTable(int width, int height)
        {
            if (width <= 0 || height <= 0)
                throw new Exception("Block dimensions can't be less than zero");
            var tabla = new Point2[height * width];
            int x = 0, y = 0;
            int pos = 0;
            tabla[pos++] = new Point2(x, y);
            while (pos < height * width)
            {
                if (x == width - 1)
                    tabla[pos++] = new Point2(x, ++y);
                else
                    tabla[pos++] = new Point2(++x, y);

                if (pos == height * width) break;

                while (x > 0 && y < height - 1)
                    tabla[pos++] = new Point2(--x, ++y);

                if (y == height - 1)
                    tabla[pos++] = new Point2(++x, y);
                else
                    tabla[pos++] = new Point2(x, ++y);

                if (pos == height * width) break;

                while (y > 0 && x < width - 1)
                    tabla[pos++] = new Point2(++x, --y);
            }
            return tabla;
        }

        public static void ZigZagToArray(short[] coefZig, short[,] coefDct, Point2[] order, int size)
        {
            int numElem = size * size;
            for (int i = 0; i < numElem; i++)
            {
                coefDct[order[i].Y, order[i].X] = coefZig[i];
            }
        }
    }

    public class Huffman
    {
        public struct CodeInfo
        {
            public ushort number;
            public uint code;
            public byte length;
        }

        public static void CreateTable(ref HuffmanTable huffmanTable)
        {
            ConvertToCanonicalCode(ref huffmanTable);
            PreparePreindexedTables(ref huffmanTable);
        }

        public static ushort ReadRunAmplitude(BitReader bReader, HuffmanTable table)
        {
            ushort code = bReader.Peek(table.maxCodeLength);
            CodeInfo currentCode = table.preIndexTable[code];
            bReader.Read(currentCode.length);
            return currentCode.number;
        }

        public static short ReadCoefValue(BitReader bReader, uint size)
        {
            if (size == 0) return 0;
            ushort specialBits = bReader.Read(size);
            return SpecialBitsToValue(specialBits, (short)size);
        }

        public static short SpecialBitsToValue(ushort number, short size)
        {
            int threshold = 1 << (size - 1);
            if (number < threshold)
            {
                return (short)(number - ((threshold << 1) - 1));
            }
            else
            {
                return (short)number;
            }
        }

        public static void ConvertToCanonicalCode(ref HuffmanTable huffmanTable)
        {
            int newCode = -1; // Para compensar con el incremento inicial dentro del for debajo
            uint prevLength = 1;
            uint newCodeIndex = 0;
            huffmanTable.table = new List<CodeInfo>();
            for (uint i = 0; i < huffmanTable.numSymbols.Length; i++)
            {
                uint codeLength = i + 1;
                for (uint j = 0; j < huffmanTable.numSymbols[i]; j++)
                {
                    var code = new CodeInfo();
                    int difLengths = (int)(codeLength - prevLength);
                    newCode = (newCode + 1) << difLengths;
                    prevLength = codeLength;
                    code.code = (uint)newCode;
                    code.length = (byte)codeLength;
                    code.number = huffmanTable.codes[newCodeIndex++];
                    huffmanTable.table.Add(code);
                }
            }
            huffmanTable.maxCodeLength = (byte)prevLength;
        }

        public static void PreparePreindexedTables(ref HuffmanTable huffmanTable)
        {
            // Codigo de datos preindizados
            // En esta parte, se construye una tabla de a que simbolos corresponden a cada codigo
            // tomando en cuenta los bits posteriores del codigo siguiente, ya que se lee del stream
            // un numero fijo de bits, esta misma tabla nos va a decir despues cuantos bits se van a extraer
            // de la lectura hecha anteriormente
            huffmanTable.preIndexTable = new CodeInfo[1U << huffmanTable.maxCodeLength];
            foreach (var codeWord in huffmanTable.table)
            {
                int shift = huffmanTable.maxCodeLength - codeWord.length;
                uint numCodes = 1U << shift;
                uint initialCode = codeWord.code << shift;
                for (uint nextCode = initialCode; nextCode < (numCodes + initialCode); nextCode++)
                {
                    huffmanTable.preIndexTable[nextCode] = codeWord;
                }
            }
        }

        public int[] Test()
        {
            int size = 32;
            int count = 40;
            var freqTable = new int[size];
            var rnd = new System.Random((int)DateTime.UtcNow.Ticks);
            for (int i = 0; i < size; i++)
            {
                freqTable[i] += rnd.Next(count);
            }
            return freqTable;
        }
    }

    public class BitReader
    {
        public const int dataSize = sizeof(ushort) * 8;
        public const int readerSize = sizeof(byte) * 8;
        public BinaryReader reader;
        public Markers lastReadMarker;
        public uint readData;
        public int availableBits;
        public bool dataPad;
        public bool lockReading;

        public bool PastEndOfFile
        {
            get { return dataPad && availableBits <= 0; }
        }

        public BinaryReader BaseBinaryReader
        {
            get { return reader; }
        }

        public BitReader(BinaryReader reader)
        {
            Flush();
            dataPad = false;
            this.reader = reader;
        }

        public ushort Peek(uint length)
        {
            if (length > dataSize) throw new Exception("Reading too many bits");
            // If we don't have as many bits as needed, read another chunk from stream
            if (length > availableBits)
            {
                byte nextChunk = 0;
                try
                {
                    while (availableBits <= length)
                    {
                        nextChunk = ReadByteOrMarker();
                        if (lockReading) break;
                        availableBits += readerSize;
                        readData = (readData << readerSize) | nextChunk;
                    }
                }
                // If already at the end of stream, use only the remaining bits we have read
                // before. Because the number of requested bits is less than the available bits,
                // the result of the clean data has the number of missing bits as zeros appended to
                // the right, as the huffman decoding phase needs a fixed number of bits to work
                catch (EndOfStreamException)
                {
                    dataPad = true;
                }
            }
            // We move data left and right in order to get only the bits we require
            uint cleanData = readData << dataSize * 2 - availableBits;
            cleanData >>= (int)(dataSize * 2 - length);
            return (ushort)cleanData;
        }

        public ushort Read(uint length)
        {
            if (length > dataSize) throw new Exception("Reading too many bits");
            ushort data = Peek(length);
            availableBits -= (int)length;
            int shift = dataSize * 2 - availableBits;
            // We move data to the left and then right in order to get only the bits we require
            readData <<= shift;
            readData >>= shift;
            return data;
        }

        public void StopReading()
        {
            if (!dataPad)
            {
                // Rewind all (whole) bytes we didn't use (including 2 from eof marker)
                // ToDo: check if this is correct
                int rewind = ((availableBits + readerSize - 1) / readerSize) + 2;
                reader.BaseStream.Seek(-rewind, SeekOrigin.Current);
            }
            Flush();
        }

        public void Flush()
        {
            availableBits = 0;
            readData = 0;
            lastReadMarker = 0;
            lockReading = false;
        }

        public byte ReadByteOrMarker()
        {
            if (!lockReading)
            {
                byte number = reader.ReadByte();
                if (number == 0xff) // Marker found
                {
                    byte markerValue = reader.ReadByte();
                    if (markerValue == 0x00) // 0xff00 is interpreted as a 0xff value
                    {
                        return number;
                    }
                    else
                    {
                        lastReadMarker = (Markers)markerValue;
                        lockReading = true;
                        return 0;
                    }
                }
                else // Not a marker, just return read value
                {
                    return number;
                }
            }
            else
            {
                return 0;
            }
        }

        public Markers SyncStreamToNextRestartMarker()
        {
            // When decoding actual image pixel data, ignore all markers except
            // restart markers, or EOI marker
            while (!((lastReadMarker >= Markers.Rs0 &&
                      lastReadMarker <= Markers.Rs7) ||
                      lastReadMarker == Markers.Eoi))
            {
                ReadByteOrMarker();
            }
            Markers tempMarker = lastReadMarker;
            Flush();
            return tempMarker;
        }

        public bool WasEoiFound()
        {
            if (lastReadMarker == Markers.Eoi && availableBits <= 0)
            {
                Flush();
                return true;
            }
            return false;
        }
    }

    public class ImageDecoder
    {
        public const int blkSize = ImgInfo.blockSize; // JPEG decoding block size is 8x8
        // Arrays used in different parts of the decoding process
        public static float[,] blockP = new float[blkSize, blkSize];
        public static short[,] coefDctQnt = new short[blkSize, blkSize];
        public static float[,] coefDct = new float[blkSize, blkSize];
        public const int DirRestartMarkerPeriod = 8;

        public static Image DecodeImage(BinaryReader reader, ImgInfo imgInfo)
        {
            // Used for reading those nasty variable length codes
            var bReader = new BitReader(reader);
            var img = new float[imgInfo.numOfComponents][,];
            imgInfo.deltaDc = new short[imgInfo.numOfComponents];
            for (int ch = 0; ch < imgInfo.numOfComponents; ch++)
                img[ch] = new float[imgInfo.height, imgInfo.width];
            // Determine the width of height of the MCU, based on the read subsampling
            // values from all channels (don't know if this is the correct way for all cases)
            // but it works for the most common ones
            var arr = imgInfo.components;
            var componentMax = arr[0];
            for (int i = 1; i < arr.Length; i++)
            {
                componentMax = new ComponentInfo()
                {
                    samplingFactorX = (byte)MathF.Max(componentMax.samplingFactorX, arr[i].samplingFactorX),
                    samplingFactorY = (byte)MathF.Max(componentMax.samplingFactorY, arr[i].samplingFactorY)
                };
            }
            int sizeMcuX = blkSize * componentMax.samplingFactorX;
            int sizeMcuY = blkSize * componentMax.samplingFactorY;
            int numMcusX = (imgInfo.width + sizeMcuX - 1) / sizeMcuX;
            int numMcusY = (imgInfo.height + sizeMcuY - 1) / sizeMcuY;
            for (int mcu = 0; mcu < numMcusX * numMcusY; mcu = NextMcuPos(imgInfo, bReader, mcu, numMcusX, numMcusY))
            {
                // X and Y coordinates of current MCU
                int mcuX = mcu % numMcusX;
                int mcuY = mcu / numMcusX;
                // Starting X and Y pixels of current MCU
                int ofsX = mcuX * sizeMcuX;
                int ofsY = mcuY * sizeMcuY;
                for (int ch = 0; ch < imgInfo.numOfComponents; ch++)
                {
                    int scaleX = componentMax.samplingFactorX / imgInfo.components[ch].samplingFactorX;
                    int scaleY = componentMax.samplingFactorY / imgInfo.components[ch].samplingFactorY;
                    for (int sy = 0; sy < imgInfo.components[ch].samplingFactorY; sy++)
                    {
                        for (int sx = 0; sx < imgInfo.components[ch].samplingFactorX; sx++)
                        {
                            DecodeBlock(bReader, imgInfo, img[ch], ch, ofsX + blkSize * sx, ofsY + blkSize * sy,
                                scaleX, scaleY);
                        }
                    }
                }
                if (bReader.PastEndOfFile) break;
            }
            Color2[,] imagen = MergeChannels(imgInfo, img);
            var bmp = new Image(imgInfo.width, imgInfo.height);
            bReader.StopReading();
            for (int y = 0; y < imgInfo.height; y++)
            {
                for (int x = 0; x < imgInfo.width; x++)
                {
                    var c = imagen[y, x];
                    var color = new Color(c.r, c.g, c.b, c.a);
                    if (MathUtils.Min(c.r, c.g, c.b) < 176)
                        bmp.SetPixel(x, y, color);
                }
            }
            return bmp;
        }

        public static int NextMcuPos(ImgInfo imgInfo, BitReader bReader, int mcu, int numMcusX, int numMcusY)
        {
            // If we are expecting a restart marker, find it in the stream,
            // reset the DC prediction variables and calculate the new MCU position
            // otherwise, just increment the position by one
            if (imgInfo.hasRestartMarkers &&
               (mcu % imgInfo.restartInterval) == imgInfo.restartInterval - 1 &&
               (mcu < numMcusX * numMcusY - 1))
            {
                Markers currRestMarker = bReader.SyncStreamToNextRestartMarker();
                if (currRestMarker == Markers.Eoi) // If EOI marker was found
                    return numMcusX * numMcusY; // Early terminate the decoding process
                int difference = currRestMarker - imgInfo.prevRestMarker;
                if (difference <= 0) difference += DirRestartMarkerPeriod;
                // For non corrupted images, difference should be always 1
                ResetDeltas(imgInfo);
                imgInfo.mcuStrip += difference;
                imgInfo.prevRestMarker = currRestMarker;
                return imgInfo.mcuStrip * imgInfo.restartInterval;
            }
            if (bReader.WasEoiFound())
            {
                return numMcusX * numMcusY;
            }
            return ++mcu;
        }

        public static Color2[,] MergeChannels(ImgInfo imgInfo, float[][,] imgS)
        {
            var img = new Color2[imgInfo.height, imgInfo.width];
            IColorspaceConverter converter;
            if (imgInfo.app14MarkerFound)
            {
                switch (imgInfo.colorMode)
                {
                    case App14ColorMode.Unknown:
                        converter = imgInfo.numOfComponents == 3 ? new Rgb() : (IColorspaceConverter)new YCbCr();
                        break;
                    case App14ColorMode.YCbCr:
                        converter = new YCbCr();
                        break;
                    case App14ColorMode.YCCK:
                        converter = new YCbCr();
                        break;
                    default:
                        converter = new Rgb();
                        break;
                }
            }
            else
            {
                converter = new YCbCr();
            }
            for (int y = 0; y < imgInfo.height; y++)
            {
                for (int x = 0; x < imgInfo.width; x++)
                {
                    Info info;
                    if (imgInfo.numOfComponents == 1) // Y
                    {
                        info.a = imgS[0][y, x];
                        info.b = 0;
                        info.c = 0;
                    }
                    else // YCbCr
                    {
                        info.a = imgS[0][y, x];
                        info.b = imgS[1][y, x];
                        info.c = imgS[2][y, x];
                    }
                    img[y, x] = converter.ConvertToRgb(info);
                }
            }
            return img;
        }

        public static void DecodeBlock(BitReader bReader, ImgInfo imgInfo, float[,] img,
            int compIndex, int ofsX, int ofsY, int scaleX, int scaleY)
        {
            int quantIndex = imgInfo.components[compIndex].quantTableId;
            short[] coefZig = GetCoefficients(bReader, imgInfo, compIndex, blkSize * blkSize);
            FileOps.ZigZagToArray(coefZig, coefDctQnt, FileOps.tablasZigzag[blkSize], blkSize);
            ImgOps.Dequant(coefDctQnt, coefDct, imgInfo.quantTables[quantIndex].table, blkSize);
            ImgOps.Fidct(coefDct, blockP, blkSize, blkSize);
            ImgOps.ResizeAndInsertBlock(imgInfo, blockP, img, blkSize, blkSize, ofsX, ofsY, scaleX, scaleY);
        }

        public static short[] GetCoefficients(BitReader bReader, ImgInfo imgInfo, int compIndex, int numCoefs)
        {
            var coefZig = new short[numCoefs];
            int acIndex = imgInfo.components[compIndex].acHuffmanTable;
            int dcIndex = imgInfo.components[compIndex].dcHuffmanTable;
            // DC coefficient
            uint runAmplitude = Huffman.ReadRunAmplitude(bReader, imgInfo.huffmanTables[0, dcIndex]);
            uint run;// = runAmplitude >> 4; // Upper nybble
            uint amplitude = runAmplitude & 0xf; // Lower nybble
            coefZig[0] = (short)(Huffman.ReadCoefValue(bReader, amplitude) + imgInfo.deltaDc[compIndex]);
            imgInfo.deltaDc[compIndex] = coefZig[0];
            // AC coefficients
            uint pos = 0;
            while (pos < blkSize * blkSize - 1)
            {
                runAmplitude = Huffman.ReadRunAmplitude(bReader, imgInfo.huffmanTables[1, acIndex]);
                // 0x00 is End of Block
                if (runAmplitude == 0x00) break;
                run = runAmplitude >> 4;
                amplitude = runAmplitude & 0xf;
                pos += run + 1;
                if (pos >= blkSize * blkSize) break;
                coefZig[pos] = Huffman.ReadCoefValue(bReader, amplitude);
            }
            return coefZig;
        }

        // If we found a restart marker, reset all DC prediction deltas, so we can
        // start anew and not depend on previous (possibly corrupted) data
        public static void ResetDeltas(ImgInfo imgInfo)
        {
            for (int i = 0; i < imgInfo.numOfComponents; i++)
            {
                imgInfo.deltaDc[i] = 0;
            }
        }
    }

    public class Rgb : IColorspaceConverter
    {
        public Color2 ConvertToRgb(Info info)
        {
            int r, g, b;
            r = (byte)Common.Clamp(info.a + 128, 0, 255);
            g = (byte)Common.Clamp(info.b + 128, 0, 255);
            b = (byte)Common.Clamp(info.c + 128, 0, 255);
            return new Color2() { a = 255, r = (byte)r, g = (byte)g, b = (byte)b };
        }

        public Info ConvertFromRgb(Color2 color)
        {
            return new Info() { a = color.r - 128, b = color.g - 128, c = color.b - 128 };
        }
    }

    public class YCbCr : IColorspaceConverter
    {
        public static float[,] mRgbYcbcr =
        {
            {  0.299f,   0.587f,   0.114f  },
            { -0.1687f, -0.3313f,  0.5f    },
            {  0.5f,    -0.4187f, -0.0813f }
        };

        public static float[,] mYcbcrRgb =
        {
            {  1f,  0f,        1.402f   },
            {  1f, -0.34414f, -0.71414f },
            {  1f,  1.772f,    0f       }
        };

        public Color2 ConvertToRgb(Info info)
        {
            byte r, g, b;
            float y = info.a + 128;
            float cb = info.b;
            float cr = info.c;
            r = (byte)Common.Clamp(y + cr * mYcbcrRgb[0, 2], 0, 255);
            g = (byte)Common.Clamp(y + cb * mYcbcrRgb[1, 1] + cr * mYcbcrRgb[1, 2], 0, 255);
            b = (byte)Common.Clamp(y + cb * mYcbcrRgb[2, 1], 0, 255);
            return new Color2() { a = 255, r = r, g = g, b = b };
        }

        public Info ConvertFromRgb(Color2 rgb)
        {
            Info yCbCr;
            // Valores YCbCr
            yCbCr.a = rgb.r * mRgbYcbcr[0, 0] + rgb.g * mRgbYcbcr[0, 1] + rgb.b * mRgbYcbcr[0, 2] - 128;
            yCbCr.b = rgb.r * mRgbYcbcr[1, 0] + rgb.g * mRgbYcbcr[1, 1] + rgb.b * mRgbYcbcr[1, 2];
            yCbCr.c = rgb.r * mRgbYcbcr[2, 0] + rgb.g * mRgbYcbcr[2, 1] + rgb.b * mRgbYcbcr[2, 2];
            //if (ycbcr.a > 255f || ycbcr.b > 255f || ycbcr.c > 255f)
            //Console.WriteLine("Valor ycbcr desbordado");
            return yCbCr;
        }
    }

    public static class Common
    {
        public static float Clamp(float num, float min, float max)
        {
            if (num < min)
                return min;
            if (num > max)
                return max;
            return num;
        }

        public static void Transpose(float[,] bloque, float[,] blTrns, int tamX, int tamY)
        {
            for (int y = 0; y < tamY; y++)
                for (int x = 0; x < tamX; x++)
                    blTrns[x, y] = bloque[y, x];
        }
    }

    public class ImgOps
    {
        public const int blkSize = ImgInfo.blockSize;
        public static float[,] coefYU = new float[blkSize, blkSize];
        public static float[,] coefUY = new float[blkSize, blkSize];
        public static float[,] coefUV = new float[blkSize, blkSize];
        public static ArraySlice<float> coefYUS = new(coefYU);
        public static ArraySlice<float> coefUYS = new(coefUY);
        public static ArraySlice<float> coefUVS = new(coefUV);
        public static float[,] tCosXU = GetTablaICos(ImgInfo.blockSize);
        public static float[,] tCosYV = GetTablaICos(ImgInfo.blockSize);
        /*static Dictionary<int, int> bitsPorNum = new Dictionary<int, int>()
        {
            {1, 0}, {2, 1}, {4, 2}, {8, 3}, {16, 4}, {32, 5}, {64, 6}
        };*/
        public static int[] bitsPorNum = [ 0, 0, 1, 0, 2, 0, 0, 0,
                                    3, 0, 0, 0, 0, 0, 0, 0,
                                    4, 0, 0, 0, 0, 0, 0, 0,
                                    0, 0, 0, 0, 0, 0, 0, 0,
                                    5, 0, 0, 0, 0, 0, 0, 0,
                                    0, 0, 0, 0, 0, 0, 0, 0,
                                    0, 0, 0, 0, 0, 0, 0, 0,
                                    0, 0, 0, 0, 0, 0, 0, 0,
                                    6
                                  ];

        public static float[,] GetTablaICos(int tam)
        {
            float[,] tablaCosUX = new float[tam, tam];
            for (int u = 0; u < tam; u++)
            {
                for (int x = 0; x < tam; x++)
                {
                    tablaCosUX[u, x] = (float)Math.Cos((2 * x + 1) * u * Math.PI / (2 * tam));
                    tablaCosUX[u, x] *= (float)Math.Sqrt(2.0 / tam);
                    if (u == 0) tablaCosUX[u, x] /= (float)Math.Sqrt(2f);
                }
            }
            return tablaCosUX;
        }

        public static void Ifct8(ArraySlice<float> bloque, ArraySlice<float> res, float[,] tIcos)
        {
            float dc = bloque[0] * tIcos[0, 0];
            float suma02 = 0;
            suma02 += bloque[4] * tIcos[4, 0];
            float suma01 = 0;
            suma01 += bloque[2] * tIcos[2, 0];
            suma01 += bloque[6] * tIcos[6, 0];
            float suma11 = 0;
            suma11 += bloque[2] * tIcos[2, 1];
            suma11 += bloque[6] * tIcos[6, 1];
            float suma00 = 0;
            suma00 += bloque[1] * tIcos[1, 0];
            suma00 += bloque[3] * tIcos[3, 0];
            suma00 += bloque[5] * tIcos[5, 0];
            suma00 += bloque[7] * tIcos[7, 0];
            float suma10 = 0;
            suma10 += bloque[1] * tIcos[1, 1];
            suma10 += bloque[3] * tIcos[3, 1];
            suma10 += bloque[5] * tIcos[5, 1];
            suma10 += bloque[7] * tIcos[7, 1];
            float suma20 = 0;
            suma20 += bloque[1] * tIcos[1, 2];
            suma20 += bloque[3] * tIcos[3, 2];
            suma20 += bloque[5] * tIcos[5, 2];
            suma20 += bloque[7] * tIcos[7, 2];
            float suma30 = 0;
            suma30 += bloque[1] * tIcos[1, 3];
            suma30 += bloque[3] * tIcos[3, 3];
            suma30 += bloque[5] * tIcos[5, 3];
            suma30 += bloque[7] * tIcos[7, 3];
            float p00 = dc + suma02;
            float p01 = dc - suma02;
            float p10 = p00 + suma01;
            float p11 = p00 - suma01;
            float p12 = p01 + suma11;
            float p13 = p01 - suma11;
            res[0] = p10 + suma00;
            res[7] = p10 - suma00;
            res[3] = p11 + suma30;
            res[4] = p11 - suma30;
            res[1] = p12 + suma10;
            res[6] = p12 - suma10;
            res[2] = p13 + suma20;
            res[5] = p13 - suma20;
        }

        public static void Fidct(float[,] bloque, float[,] bloqueDct, int tamX, int tamY)
        {
            var bloqueS = new ArraySlice<float>(bloque);
            // Sacamos el IDCT de cada fila del bloque
            for (int y = 0; y < tamY; y++)
            {
                Ifct8(bloqueS.GetSlice(y), coefYUS.GetSlice(y), tCosXU);
            }
            Common.Transpose(coefYU, coefUY, tamX, tamY);
            // Ahora sacamos el DCT por columna de los resultados anteriores
            for (int u = 0; u < tamX; u++)
            {
                Ifct8(coefUYS.GetSlice(u), coefUVS.GetSlice(u), tCosYV);
            }
            for (int v = 0; v < tamY; v++)
            {
                for (int u = 0; u < tamX; u++)
                {
                    bloqueDct[v, u] = (float)Math.Round(coefUV[u, v]);
                }
            }
        }

        public static void Idct(float[,] bloque, float[,] bloqueDct, int tamX, int tamY)
        {
            int u, v, x, y;
            float suma, suma2;
            for (v = 0; v < tamY; v++)
            {
                for (u = 0; u < tamX; u++)
                {
                    for (y = 0, suma2 = 0; y < tamY; y++)
                    {
                        for (x = 0, suma = 0; x < tamX; x++)
                        {
                            suma += bloque[y, x] * tCosXU[x, u];
                        }
                        suma2 += suma * tCosYV[y, v];
                    }
                    bloqueDct[v, u] = (float)Math.Round(suma2);
                }
            }
        }

        public static void MostrarBordes(float[,] coefDct, int tam)
        {
            for (int y = 0; y < tam; y++)
                coefDct[y, tam - 1] = 96f;
            for (int x = 0; x < tam; x++)
                coefDct[tam - 1, x] = 96f;
        }

        public static void ResizeAndInsertBlock(ImgInfo imgInfo, float[,] block, float[,] imagen, int tamX, int tamY, int ofsX, int ofsY, int scaleX, int scaleY)
        {
            // Nearest neighbor interpolation
            // For staircase FTW
            if (ofsX < imgInfo.width && ofsY < imgInfo.height)
            {
                for (int j = 0; j < tamY; j++)
                {
                    for (int i = 0; i < tamX; i++)
                    {
                        for (int jj = 0; jj < scaleY; jj++)
                        {
                            for (int ii = 0; ii < scaleX; ii++)
                            {
                                int posYimg = j * scaleY + ofsY + jj;
                                int posXimg = i * scaleX + ofsX + ii;
                                if (posYimg < imgInfo.height && posXimg < imgInfo.width)
                                {
                                    imagen[posYimg, posXimg] = block[j, i];
                                }
                            }
                        }
                    }
                }
            }
        }

        public static void Dequant(short[,] pixQnt, float[,] coefDct, ushort[] matriz, int tam)
        {
            for (int y = 0; y < tam; y++)
            {
                for (int x = 0; x < tam; x++)
                {
                    coefDct[y, x] = pixQnt[y, x] * matriz[y * tam + x];
                }
            }
        }
    }

    public class ArraySlice<T>
    {
        public readonly T[,] arr;

        public int firstDimension;

        public ArraySlice(T[,] arr)
        {
            this.arr = arr;
        }

        public T this[int index]
        {
            get { return arr[firstDimension, index]; }
            set { arr[firstDimension, index] = value; }
        }

        public ArraySlice<T> GetSlice(int firstDimension)
        {
            this.firstDimension = firstDimension;
            return this;
        }
    }
}